package com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.repository;

import java.util.*;
import java.util.stream.Collectors;

import com.google.common.collect.Lists;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.NodeOperEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.NodeOperEventTypeEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.ProcessInstanceStatusEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.TaskLinkStatusEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.threadlocal.CommonOperateLogHolder;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.UserService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.impl.task.SyncProcessTaskProcessor;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.FlowUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.RollbackValidator;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.RollbackValidator.ValidateResult;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ConstanceVariable;
import com.iwhalecloud.citybrain.flow.platform.manager.common.client.HttpTaskRollbackDto;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.enums.CommentTypeEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.exception.TaskRollbackException;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowActivityJumpHistoryVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowMultiInstanceTaskTraceVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.ModelCustomConfigDomain;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.ModelNextNodeCandidateSettings;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.monitor.FlowTaskTraceVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.task.TaskRollbackVo;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.BaseProcessService;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.userentity.UserInfo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.*;
import groovy.lang.Lazy;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.util.Asserts;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

/**
 * 回退节点，适用场景如下。
 * 1. 源节点和目标节点在同一个路径，并且不跨网关。
 * -> c -> e
 * s -> b -> k -> |         | -> g -> end
 * -> d -> f
 * <p>
 * 场景            可行
 * k -> b..........Y
 * e -> c..........Y
 * c -> k..........N
 * g -> e/f........N
 * <p>
 * 2. 源节点和目标节点可以是会签或者普通节点
 * <p>
 * 3. 会签节点的特殊处理
 * runtimeService.deleteMultiInstanceExecution先删除会签任务下的子execution，才能删除root execution
 *
 * @author lich
 * @date 2021/11/8
 */
@Service
public class FlowRollbackServiceImpl extends BaseProcessService implements FlowRollbackService {

    @Autowired
    private FlowTaskTraceRepository flowTaskTraceRepository;

    @Autowired
    private FlowMultiInstanceTaskTraceRepository multiInstanceTaskTraceRepository;

    @Autowired
    private FlowActivityJumpHistoryRepository flowActivityJumpHistoryRepository;

    @Autowired
    private SyncProcessTaskProcessor syncProcessTaskProcessor;

    @Autowired
    private FlowNextNodeCandidateRepository flowNextNodeCandidateRepository;

    @Autowired
    private UserService userService;

    @Lazy
    @Autowired
    private FlowProcessInstanceHandleRelRepository handleRelRepository;

    @Autowired
    private FlowTaskTraceRepository taskTraceService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rollbackToPreNode(HttpTaskRollbackDto taskRollbackVo) throws Exception {

        this.executeNodeOperEvent(taskRollbackVo.getWorkItemId()
                , taskRollbackVo.getParticipantId(), NodeOperEnum.ROLLBACK_TO_PRE, NodeOperEventTypeEnum.BEFORE);

        rollbackToPreNode(taskRollbackVo.getWorkItemId(), taskRollbackVo.getComment(),
                taskRollbackVo.getParticipantId());

        this.executeNodeOperEvent(taskRollbackVo.getWorkItemId()
                , taskRollbackVo.getParticipantId(), NodeOperEnum.ROLLBACK_TO_PRE, NodeOperEventTypeEnum.AFTER);

    }

    @Override
    public void rollbackToPreNode(TaskRollbackVo taskRollbackVo, String userCode) throws Exception {
        CommonOperateLogHolder.setFromSdk(true);
        try {
            rollbackToPreNode(taskRollbackVo.getTaskId(), taskRollbackVo.getComment(), userCode);
        } finally {
            CommonOperateLogHolder.removeAll();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rollbackToFirstNode(HttpTaskRollbackDto taskRollbackDto) throws Exception {

        this.executeNodeOperEvent(taskRollbackDto.getWorkItemId()
                , taskRollbackDto.getParticipantId(), NodeOperEnum.ROLLBACK_TO_FIRST, NodeOperEventTypeEnum.BEFORE);

        Task task = taskService.createTaskQuery().taskId(taskRollbackDto.getWorkItemId()).singleResult();

        Assert.notNull(task, "根据任务编号找不到任务信息");

        List<UserTask> firstTasks = FlowUtils.getInstance().getFirstTasks(task.getProcessDefinitionId());

        Assert.notNull(firstTasks, "无法找到第一个用户任务");
        Assert.notNull(firstTasks.size() == 0, "无法找到第一个用户任务");
        if (firstTasks.size() > 1) {
            throw new TaskRollbackException("拟稿人包含多个用户任务，不支持直接跳转到多个用户任务");
        }

        rollbackToTargetNode(task, taskRollbackDto.getComment(), taskRollbackDto.getParticipantId(),
                firstTasks.stream().map(UserTask::getId).collect(Collectors.toList()));

        this.executeNodeOperEvent(taskRollbackDto.getWorkItemId()
                , taskRollbackDto.getParticipantId(), NodeOperEnum.ROLLBACK_TO_FIRST, NodeOperEventTypeEnum.AFTER);

    }

    @Override
    public void rollbackToTargetNode(HttpTaskRollbackDto taskRollbackDto) throws Exception {

        Task task = taskService.createTaskQuery().taskId(taskRollbackDto.getWorkItemId()).singleResult();

        Assert.notNull(task, "根据任务编号找不到任务信息");

        Set<String> targetNodes = taskRollbackDto.getTargetActivities();
        rollbackToTargetNode(task, taskRollbackDto.getComment(), taskRollbackDto.getParticipantId(),
                Lists.newArrayList(targetNodes));

    }

    @Override
    public void rollbackToPreNode(String taskId, String comment, String userCode) throws Exception {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();

        Assert.notNull(task, "根据任务编号找不到任务信息");

        String currentNodeCode = task.getTaskDefinitionKey();

        // 如果是第一个task，那么不让他跳转到前一个task
        List<UserTask> firstTasks = FlowUtils.getInstance().getFirstTasks(task.getProcessDefinitionId());
        if (!CollectionUtils.isEmpty(firstTasks)) {
            Set<String> firstTaskIds = firstTasks.stream().map(UserTask::getId).collect(Collectors.toSet());
            if (firstTaskIds.contains(task.getTaskDefinitionKey())) {
                throw new TaskRollbackException(
                        String.format("在流程定义中，%s已经第一个用户节点了，不能再回退到上一节点", task.getTaskDefinitionKey()));
            }
        }

        List<FlowElement> targetFlowElementList = FlowUtils.getInstance().getFirstFrontElement(currentNodeCode,
                task.getProcessDefinitionId());

        Assert.notEmpty(targetFlowElementList, "回退失败，无法找到回退的目标节点");

        rollbackToTargetNode(task, comment, userCode, targetFlowElementList.stream()
                .map(FlowElement::getId).collect(Collectors.toList()));
    }

    @Override
    public void takeBack(String taskId, String comment, String userCode) throws Exception {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();

        Assert.notNull(task, "根据任务编号找不到任务信息");

        String currentNodeCode = task.getTaskDefinitionKey();

        List<FlowElement> targetFlowElementList = FlowUtils.getInstance().getFirstFrontElement(currentNodeCode,
                task.getProcessDefinitionId());

        Assert.notEmpty(targetFlowElementList, "拿回失败，无法找到拿回的目标节点");

        fromNodetakeBack(task, comment, userCode, targetFlowElementList.stream()
                .map(FlowElement::getId).collect(Collectors.toList()));
    }

    //完全复制rollbackToTargetNode ，只是去掉是否必填意见的判断
    private void fromNodetakeBack(Task task, String comment, String userCode, List<String> targetNodes) {
        comment = "[拿回场景]" + (comment == null ? "" : comment);
        gotoTargetNodeInternal(task, comment, userCode, targetNodes);
    }

    private void gotoTargetNodeInternal(Task task, String comment, String userCode, List<String> targetNodes) {
        String processInstanceId = task.getProcessInstanceId();

        RollbackValidator rollbackValidator = new RollbackValidator(
                processInstanceId,
                task.getTaskDefinitionKey(),
                targetNodes
        );
        rollbackValidator.setRepositoryService(repositoryService);
        rollbackValidator.setRuntimeService(runtimeService);

        rollbackValidator.executeValidation();
        ValidateResult validateResult = rollbackValidator.provideValidateResult();

        if (!validateResult.isSuccess()) {
            logger.error("流程回滚验证失败：{}", validateResult.getMessage());
            throw new TaskRollbackException(validateResult.getMessage());
        }

        if (!validateResult.isJumpToFirst() && validateResult.isCrossGateway()) {
            // 如果不是回到第一个节点，且又跨网关，那么回退行为不可预测，从而禁止
            throw new TaskRollbackException(
                    String.format("目标任务（%s）不在源任务节点（%s）之前，或者跨越了并行或者包含网关",
                            rollbackValidator.getTargetNameStr(), task.getName()));
        }

        List<Execution> childExecutionList = retrieveOpenExecutions(processInstanceId);

        //1. 如果当前任务为多实例任务，首先清理子execution，然后更新其multi instance task trace状态
        cleanUpMultiInstanceTask(task);

        List<Execution> movableExecutions = filterMovableExecution(childExecutionList, processInstanceId, task, validateResult);

        //2. 更新flowTaskTrace表
        /*updateTaskTraceStatus(movableExecutions);*/
        // 回退到目标节点，更新flowTaskTrace表的处理人信息
//        updateTaskTraceStatusAndOperator(movableExecutions, userCode);
        updateTaskTraceStatusAndOperator(task.getId(), userCode);

        //3. 再回退所有execution到目标node
        if (movableExecutions.size() == 1) {
            moveSingleExecutionToActivityIds(processInstanceId, movableExecutions.get(0), targetNodes);
        } else {
            if (targetNodes.size() > 1) {
                throw new TaskRollbackException("不支持跳转到多个execution跳转到多个目标节点");
            }
            moveMultiExecutionToSingleActivityId(processInstanceId, movableExecutions, targetNodes.get(0));
        }

        //4. 记录跳转信息
        recordJumpHistory(task.getTaskDefinitionKey(), targetNodes, rollbackValidator.getProcessInstance(), comment, userCode);

        //5. 清除由目标节点指定的节点候选人信息
        cleanFlowNodeCandidateSettings(processInstanceId);

        // 6.退回成功之后,将同步任务状态
        List<Execution> mutiTaskExecution = childExecutionList.stream().filter(x -> !movableExecutions.stream().map(y -> y.getId()).collect(Collectors.toList()).contains(x.getId())).collect(Collectors.toList());
        this.syncDataByScene(mutiTaskExecution, processInstanceId, task);

        // 7. 退出成功之后,清除由目标节点指定的阅办信息
        this.cleanReadDoInfos(processInstanceId, targetNodes.get(0));
    }


    private void rollbackToTargetNode(Task task, String comment, String userCode, List<String> targetNodes) {

        if (StringUtils.isBlank(comment)) {
            boolean required = checkRequiredRollbackComment(task);
            if (required) {
                throw new TaskRollbackException("退回必填意见");
            }
        } else {
            this.addComment(task.getId(), userCode, task.getProcessInstanceId(), CommentTypeEnum.BH.getName(), comment);
        }
        comment = "[退回场景]" + (comment == null ? "" : comment);
        gotoTargetNodeInternal(task, comment, userCode, targetNodes);
    }

    private boolean checkRequiredRollbackComment(Task task) {
        String processDefinitionId = task.getProcessDefinitionId();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        if (Objects.nonNull(processDefinition)) {
            String deployId = processDefinition.getDeploymentId();
            List<ModelCustomConfigDomain> modelCustomConfigDomainList = flowReModelCustomConfigRepository.listModelConfigByDeployIdAndType(deployId
                    , task.getTaskDefinitionKey(), ConstanceVariable.REQUIRED_ROLLBACK_COMMENT);
            if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(modelCustomConfigDomainList)) {
                String value = modelCustomConfigDomainList.get(0).getConfig();
                if (Objects.equals(value, "1")) {
                    return true;
                }
            }
        }
        return false;
    }

    private void recordJumpHistory(String sourceActivityId, List<String> finalTargetActivityIds,
                                   ProcessInstance processInstance, String comment, String userCode) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        FlowElement sourceElement = bpmnModel.getFlowElement(sourceActivityId);
        for (String target : finalTargetActivityIds) {
            FlowElement targetElement = bpmnModel.getFlowElement(target);
            FlowActivityJumpHistoryVo record = new FlowActivityJumpHistoryVo();
            record.setProcessInstanceId(processInstance.getProcessInstanceId());
            record.setFromActivityId(sourceElement.getId());
            record.setFromActivityName(sourceElement.getName());
            record.setToActivityId(targetElement.getId());
            record.setToActivityName(targetElement.getName());
            record.setComment(comment);
            record.setCreateName(userCode);
            flowActivityJumpHistoryRepository.insertActivityJumpHistory(record);
        }
    }

    private void cleanFlowNodeCandidateSettings(String processInstanceId) {

        // 再根据流程实例ID获取流程实例对象
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (Objects.nonNull(processInstance)) {

            // 获取是否需要再次进行后选人设置
            List<ModelCustomConfigDomain> cleanCandidateUserSettings = flowReModelCustomConfigRepository.listModelConfigByParams(processInstance.getDeploymentId(), null, ConstanceVariable.CLEAN_CANDIDATE_USER_SETTING);

            // 查询已经走过的待办任务列表
            List<FlowTaskTraceVo> flowTaskTraceVos = taskTraceService.listByProcessInstanceId(processInstanceId);

            // 如果该流程的任务节点上该属性被设置了,则需要判断是否需要清除,为空或0都是需要清除的,如果是1时,则不需要清除,默认是0
            // 20220714 实际上系统对未勾选该属性的节点，没有生产默认值为0的记录
            // 否则,如果任务节点未配置该属性,则直接清除操作
            if (CollectionUtils.isEmpty(cleanCandidateUserSettings)) {

                // 如果不为空,则取出节点编码并清除该节点之前设置的任务节点后选人
                if (!CollectionUtils.isEmpty(flowTaskTraceVos)) {
                    for (FlowTaskTraceVo flowTaskTraceVo : flowTaskTraceVos) {
                        this.deleteFlowCandidateSettingsByParams(processInstanceId, flowTaskTraceVo.getActivityId());
                    }
                }
            } else {
                // 需要判断过滤出需要清除的节点,
                List<String> nodeCodes = flowTaskTraceVos.stream().map(FlowTaskTraceVo::getActivityId).distinct().collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(flowTaskTraceVos)) {
                    for (FlowTaskTraceVo flowTaskTraceVo : flowTaskTraceVos) {
                        for (ModelCustomConfigDomain setting : cleanCandidateUserSettings) {
                            if (Objects.equals(flowTaskTraceVo.getActivityId(), setting.getNodeCode()) &&
                                    Objects.equals("1", setting.getConfig())) {
                                nodeCodes.remove(flowTaskTraceVo.getActivityId());
                            }
                        }
                    }
                }

                // 需要清除的节点集合不为空时,则需要进行清除操作
                if (!CollectionUtils.isEmpty(nodeCodes)) {
                    for (String nodeCode : nodeCodes) {
                        this.deleteFlowCandidateSettingsByParams(processInstanceId, nodeCode);
                    }
                }
            }
        }
    }

    /**
     * 根据流程实例和来源节点编码删除
     *
     * @param processInstanceId
     * @param nodeCode
     */
    private void deleteFlowCandidateSettingsByParams(String processInstanceId, String nodeCode) {
        List<ModelNextNodeCandidateSettings> flowNextNodeCandidateList = flowNextNodeCandidateRepository.selectByProcessInstanceIdAndNodeCode(processInstanceId, nodeCode);
        if (!CollectionUtils.isEmpty(flowNextNodeCandidateList)) {
            for (ModelNextNodeCandidateSettings candidate : flowNextNodeCandidateList) {
                flowNextNodeCandidateRepository.deleteByPrimaryKey(candidate.getId());
            }
        }
    }


    /**
     * 清除阅办信息列表
     *
     * @param processInstanceId
     * @param fromNodeCode
     */
    private void cleanReadDoInfos(String processInstanceId, String fromNodeCode) {
        try {
            handleRelRepository.deleteInfo(processInstanceId, fromNodeCode);
        } catch (Exception e) {
            logger.error("删除流程阅办信息失败：{}", e.getMessage());
        }
    }

    private void moveSingleExecutionToActivityIds(String processInstanceId, Execution sourceExecution,
                                                  List<String> targetNodes) {
        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(processInstanceId)
                .moveSingleExecutionToActivityIds(sourceExecution.getId(), targetNodes)
                .changeState();
    }

    private void moveMultiExecutionToSingleActivityId(String processInstanceId, List<Execution> sourceExecutions,
                                                      String targetNode) {
        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(processInstanceId)
                .moveExecutionsToSingleActivityId(
                        sourceExecutions.stream().map(Execution::getId).collect(Collectors.toList()),
                        targetNode)
                .changeState();
    }

    private List<Execution> retrieveOpenExecutions(String processInstanceId) {
        return runtimeService.createExecutionQuery()
                .processInstanceId(processInstanceId).onlyChildExecutions().list();
    }

    private List<Execution> filterMovableExecution(
            List<Execution> executions,
            String processInstanceId, Task currentTask, ValidateResult validateResult) {
        //我们只能移动那些在流程实例下的execution
        List<Execution> executionList = executions.stream()
                .filter(e -> StringUtils.equals(e.getParentId(), processInstanceId)).collect(Collectors.toList());

        // 不从网关移动出去，或者不从外部移动到网关，那么:
        // ****理论上仅应该有一个execution被移动***
        // 除非是从并行网关回退到拟稿人
        if (executionList.size() > 1) {
            logger.warn("发现多个可以移动的execution");
            executionList.forEach(e -> {
                logger.warn("  execution, id: {}, at activity: {}", e.getId(), e.getActivityId());
            });
        }

        if (!validateResult.isCrossGateway()) {
            for (Execution execution : executionList) {
                if (Objects.equals(execution.getActivityId(), currentTask.getTaskDefinitionKey())) {
                    return Lists.newArrayList(execution);
                }
            }
        } else {
            Asserts.check(validateResult.isJumpToFirst(), "在支持跨网关的情况下，我们仅支持回退到拟稿人（即第一个节点）");
            return executionList;
        }

        throw new TaskRollbackException("没有发现可以跳转的execution");
    }

    private void updateTaskTraceStatus(List<Execution> executions) {
        for (Execution execution : executions) {
            Task childTask = taskService.createTaskQuery().executionId(execution.getId()).singleResult();
            if (Objects.nonNull(childTask)) {
                flowTaskTraceRepository.updateStatusToReject(childTask.getId());
            }
        }
    }

    // 修复回退后节点不展示驳回责任人信息缺陷
    // 2023-07-02 会签节点驳回时未处理的任务状态不更新
    private void updateTaskTraceStatusAndOperator(String taskId, String userCode) {
        UserInfo userInfo = userService.getUserInfoObj(userCode);
        flowTaskTraceRepository.updateStatusAndOperator(taskId, userCode, userInfo.getUserName());

    }

    // 修复回退后节点不展示驳回责任人信息缺陷
    private void updateTaskTraceStatusAndOperator(List<Execution> executions, String userCode) {
        for (Execution execution : executions) {
            Task childTask = taskService.createTaskQuery().executionId(execution.getId()).singleResult();
            UserInfo userInfo = userService.getUserInfoObj(userCode);
            if (Objects.nonNull(childTask)) {
                flowTaskTraceRepository.updateStatusAndOperator(childTask.getId(), userCode, userInfo.getUserName());
            }
        }
    }

    private void cleanUpMultiInstanceTask(Task task) {
        Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
        if (execution == null) {
            return;
        }

        FlowMultiInstanceTaskTraceVo multiInstanceRoot = multiInstanceTaskTraceRepository.getByRootExecutionId(
                execution.getParentId());
        if (multiInstanceRoot == null) {
            return;
        }

        //说明是一个多实例任务，那么我们需要先更新多实例各个任务的状态，然后删除各实例任务的execution
        List<Task> tasks = taskService.createTaskQuery()
                .processInstanceId(task.getProcessInstanceId())
                .taskDefinitionKey(task.getTaskDefinitionKey())
                .list();
        if (!CollectionUtils.isEmpty(tasks)) {
            tasks.forEach(t -> {
                //该会签节点里面所有任务状态为拒绝
                flowTaskTraceRepository.updateStatusToReject(t.getId());
            });
        }
        List<Execution> executions =
                runtimeService.createExecutionQuery().parentId(multiInstanceRoot.getRootExecutionId()).list();
        if (!CollectionUtils.isEmpty(executions)) {
            //必须删除会签任务下所有execution，才能保证root execution成功删除
            executions.forEach(e -> runtimeService.deleteMultiInstanceExecution(e.getId(), true));
        }

        markMultiInstanceRootTaskDone(multiInstanceRoot);
    }

    private void markMultiInstanceRootTaskDone(FlowMultiInstanceTaskTraceVo multiInstanceRoot) {
        if (multiInstanceRoot != null) {
            multiInstanceRoot.setNumOfCompletedInstances(multiInstanceRoot.getNumOfInstances());
            multiInstanceRoot.setNumOfActiveInstances(0);
            multiInstanceRoot.setLinkStatus(TaskLinkStatusEnum.DONE.getCode());
            multiInstanceRoot.setUpdateTime(new Date());
            multiInstanceTaskTraceRepository.updateMultiInstanceTaskTrace(multiInstanceRoot);
        }
    }

    private void syncDataByScene(List<Execution> executions, String processInstanceId, Task task) {
        List<String> taskIds = new ArrayList<>();
        // 校验是否是会签节点,如果是会签节点,则需要更新待办组件中当前节点其余会签任务的状态，否则只需更新单条任务状态
        Boolean isMultiTask = StringUtils.isNotBlank(processInstanceId) ? FlowUtils.getInstance().isMultiTaskActivity(processInstanceId, task.getTaskDefinitionKey()) : false;
        if (isMultiTask && !CollectionUtils.isEmpty(executions)) {
            List<String> multiTaskIds = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).list().stream().filter(x -> executions.stream().map(y -> y.getId()).collect(Collectors.toList()).contains(x.getExecutionId())).map(x -> x.getId()).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(multiTaskIds)) {
                return;
            }
            taskIds.addAll(multiTaskIds);
            syncProcessTaskProcessor.syncDataByScene(null, taskIds, ProcessInstanceStatusEnum.ROLLBACK.getCode(), TaskLinkStatusEnum.DONE.getCode(), isMultiTask);
        } else {
            taskIds.add(task.getId());
            syncProcessTaskProcessor.syncDataByScene(null, taskIds, ProcessInstanceStatusEnum.ROLLBACK.getCode(), TaskLinkStatusEnum.DONE.getCode());
        }
    }
}
